package service.store

import org.slf4j.Logger
import org.slf4j.LoggerFactory

import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.serializer.SerializerFeature

import org.paic.util.PropHelper

import org.paic.d.D

class PluginManager {
	static Logger log = LoggerFactory.getLogger(PluginManager.class)

	final static String CONF_FILE = '/config/plugin.properties'
	static Properties conf = PropHelper.loadProperties(CONF_FILE)
	static void refreshConf(){
		conf = PropHelper.loadProperties(CONF_FILE)
	}

	final static String pluginDir = 'plugins'
	final static String pluginPackFile = 'pack.gy'
	final static String pluginInstalledFile = 'installed.json'

	static Map getFileInfo(String filePath){
		def f = new File(pluginDir, filePath)
		if(!f.exists())
			return null

		try{
			if(f.name.endsWith('.json')){
				return JSON.parseObject(f.text)
			}else if(f.name.endsWith('.gy')){
				return new GroovyShell().evaluate(f.text)
			}
		}catch(ex){
			log.error('plugin file not valid - ' + filePath, ex)
			return null
		}
	}

	// installed plugins
	static Map getPluginInstalled(){
		getFileInfo(pluginInstalledFile)
	}

	private static void outputPluginInstalledFile(Map item){
		String txt = JSON.toJSONString(item, SerializerFeature.PrettyFormat)
		def f = new File(pluginDir, pluginInstalledFile)
		f.text = txt
	}

	static boolean isPluginInstalled(Map pack){
		def item = getPluginInstalled()
		if(!item || !item.list)
			return false

		def target = item.list.find{it.code == pack.code && it.version == pack.version}
		return target != null
	}

	static Map addPluginAsInstalled(Map pack){
		def item = getPluginInstalled()
		if(!item)
			item = [list: []]
		if(!item.list)
			item.list = []

		item.lastModified = new Date().format('yyyy-MM-dd HH:mm:ss')

		def target = item.list.find{it.code == pack.code && it.version == pack.version}
		if(target)
			target.putAll(pack)
		else
			item.list << pack

		outputPluginInstalledFile(item)

		item
	}

	static Map removePluginAsInstalled(Map pack){
		def item = getPluginInstalled()
		if(!item)
			item = [list: []]

		item.lastModified = new Date().format('yyyy-MM-dd HH:mm:ss')

		def target = item.list.find{it.code == pack.code && it.version == pack.version}
		if(target)
			item.list.remove(target)

		outputPluginInstalledFile(item)

		item
	}

	// get one plugin description
	static Map getPluginPack(String code){
		getFileInfo(code + '/' + pluginPackFile)
	}

	// build one plugin files into a zip file
	static Map pack(Map conf, Map pack){
		def r = [:]

		def tmpDir = pluginDir + '/tmp/'
		String packFilePath = pluginDir + '/' + pack.code + '-' + pack.version + '.zip'

		def ant = new AntBuilder()

		// clear first
		ant.delete dir: tmpDir
		ant.mkdir dir: tmpDir
		ant.delete file: packFilePath

		ant.copy file: pluginDir + '/' + pack.code + '/' + pluginPackFile, tofile: tmpDir + pluginPackFile

		def projectDir = new File('.')
		String projectDirPath = projectDir.absolutePath

		projectDir.eachFileRecurse{
			if(!it.isFile())
				return 

			String pathRelative = it.absolutePath.substring(projectDirPath.size() + 1).replaceAll("\\\\", '/')
			if(pack.files.any{filePat ->
				pathRelative ==~ filePat
			}){
				ant.copy file: it.absolutePath, tofile: tmpDir + pathRelative
			}
		}

		ant.zip basedir: tmpDir, destfile: packFilePath
		ant.delete dir: tmpDir

		r.flag = true
		r.packFilePath = packFilePath
		r
	}

	public static Map doWithPack(Map pack, String type){
		boolean isOk = extract(pack)

		if(!isOk)
			return [flag: false, msg: 'extract faiiled']

		copyBatch(pack)
		
		exeSql(pack, type)
	}

	public static Map installPack(Map pack){
		doWithPack(pack, 'init')
	}

	public static Map updatePack(Map pack){
		doWithPack(pack, 'update')
	}

	public static Map removePack(Map pack){
		def r = exeSql(pack, 'remove')
		if(!r.flag)
			return r
		
		def projectDir = new File('.')
		String projectDirPath = projectDir.absolutePath

		def ant = new AntBuilder()
		projectDir.eachFileRecurse{
			if(!it.isFile())
				return 

			String pathRelative = it.absolutePath.substring(projectDirPath.size() + 1).replaceAll("\\\\", '/')
			if(pack.files.any{filePat ->
				pathRelative ==~ filePat
			}){
				ant.delete file: it.absolutePath
			}
		}

		[flag: true]
	}

	// db init/update/remove when plugin install/update/remove
	// eg. src/db/wx-messager/init.sql
	private static Map exeSql(Map pack, String type){
		def file = new File('src/db/' + pack.code + '/' + type + '.sql')
		if(!file.exists()){
			log.info('plugin sql skiped - ' + pack + ' - ' + type)
			return [flag: true]
		}

		def dao = D.gen()
		try{
		    dao.exe(file.text)
			log.info('plugin sql exe ok - ' + pack + ' - ' + type)
			return [flag: true]
		}catch(e){
			log.error('plugin sql exe failed!' + pack + ' - ' + type, e)
			return [flag: false, msg: 'sql exe failed - ' + e.message]
		}
	}

	private static void copyBatch(Map pack){
		String tmpInstalledPath = pluginDir + '/tmp-install'

		def ant = new AntBuilder()

		ant.copy todir: '.', {
			fileset dir: tmpInstalledPath, {
				exclude name: 'pack.gy'
			}
		}

		String targetPluginDir = pluginDir + '/' + pack.code
		ant.mkdir dir: targetPluginDir

		ant.copy todir: targetPluginDir, {
			fileset dir: tmpInstalledPath, {
				include name: 'pack.gy'
			}
		}
	}

	private static boolean extract(Map pack){
		String packFilePath = pluginDir + '/' + pack.code + '-' + pack.version + '.zip'
		def file = new File(pluginDir)
		if(!file.exists()){
			return false
		}

		def ant = new AntBuilder()

		String tmpInstalledPath = pluginDir + '/tmp-install'
		ant.delete dir: tmpInstalledPath
		ant.mkdir dir: tmpInstalledPath

		ant.unzip src: packFilePath, dest: tmpInstalledPath

		true
	}
}